iT邦幫忙

2021 iThome 鐵人賽

DAY 5
1
自我挑戰組

JS30 學習日記系列 第 5

Day 5 - Flex Panels Image Gallery

  • 分享至 

  • xImage
  •  

前言

JS 30 是由加拿大的全端工程師 Wes Bos 免費提供的 JavaScript 簡單應用課程,課程主打 No FrameworksNo CompilersNo LibrariesNo Boilerplate 在30天的30部教學影片裡,建立30個JavaScript的有趣小東西。

另外,Wes Bos 也很無私地在 Github 上公開了所有 JS 30 課程的程式碼,有興趣的話可以去 fork 或下載。


本日目標

利用 flexboxtransition 等 CSS 屬性,搭配 JS 監聽 transitionendclick 事件,最終實作出一個美觀的 Image Gallery。


解析程式碼

HTML 部分

由最外層的.panels包覆住內部的5個.panel所形成的一個巢狀結構。

<div class="panels">
    <div class="panel panel1">
      <p>Hey</p>
      <p>Let's</p>
      <p>Dance</p>
    </div>
    <div class="panel panel2">
      <p>Give</p>
      <p>Take</p>
      <p>Receive</p>
    </div>
    <div class="panel panel3">
      <p>Experience</p>
      <p>It</p>
      <p>Today</p>
    </div>
    <div class="panel panel4">
      <p>Give</p>
      <p>All</p>
      <p>You can</p>
    </div>
    <div class="panel panel5">
      <p>Life</p>
      <p>In</p>
      <p>Motion</p>
    </div>
</div>

CSS 部分

(僅說明影片中更動的部分)

先將最外層的 .panels 顯示類型設定為 flex,它同時也作為一個 flex-container 包覆住內部的五個 flex-items 也就是 .panel

.panels {
      /*其餘略過*/
      display: flex;
}

flexflex-growflex-shrinkflex-basis 的簡寫,只有指定一個值給 flex 時,則代表設定的是 flex-grow,其餘屬性以預設值帶入。

flex-grow 可以指定 flex-container 的剩餘空間該如何分配,下面所有的 .panelflex-grow 都是 1,也就是均勻分配剩餘空間。

接著,將每一個 .panel 的顯示類型都設定為 flex (此時的.panel 對下面的<p></p>來說就是 flex-container) 並將其下的 flex-item 在水平、鉛直方向都置中。

最後,設定 flex-directionflex-boxmain-axis 更改為直列。

.panel {
      flex: 1; /*將每個 flex-item 的大小都設為一樣並填滿*/
      display: flex;  
      justify-content: center; /*在水平方向置中*/
      align-items: center;/*在鉛直方向置中*/
      flex-direction: column; 
}

.panel (flex-container)下的 <p></p> (flex-item) 的 flex 屬性設定為 1、0、auto,也就是均勻分配 flex-container 的剩餘空間、flex-item 長度超過 flex-container 時的收縮量設為 0、flex-itemflex container 的初始大小設為自動(auto)。

接著,也將<p></p>當作是一個 flex-container,設定顯示類型為 flex。透過 justify-contentalign-items,將標籤內的文字(flex-item) 水平、鉛直置中排列。

.panel > * {
      /*其餘省略*/
      flex: 1 0 auto;
      display: flex;
      justify-content: center;
      align-items: center;
}

初始狀態,分別將在 .panel 上方和下方的 <p></p> 都各上移、下移 100% 以達到隱藏的效果。

.panel > *:first-child{
      transform: translateY(-100%);/*上移*/
}
.panel > *:last-child{
      transform: translateY(100%);/*下移*/
}

接著設定當.panel上有open-active這個 class 時,就將原本隱藏的文字分別下移和上移顯示出來。

.panel.open-active > *:first-child{
      transform: translateY(0);
}
.panel.open-active > *:last-child{
      transform: translateY(0);
}

.panel開啟時,將內部文字放大為 40px 並將 flex-container 剩餘分配的位置變為原來的5倍。

.panel.open {
      font-size: 40px;
      flex: 5;
}
補充資料:

CSS flex 屬性
圖解 Flexbox 基本屬性
FLEXBOX FROGGY-學習 flexbox 的小遊戲

JS 部分

取得所有的.panel 並放到 NodeList 'panels' 中。

const panels = document.querySelectorAll('.panel');

panels 中的每一個.panel都註冊兩個事件監聽器,當 clicktransitionend 事件發生時,就分別以 toggleOpentoggleActive 方法進行事件處理。

panels.forEach(panel => panel.addEventListener('click',toggleOpen));
panels.forEach(panel => panel.addEventListener('transitionend',toggleActive));

點擊任意一個.paneltoggleOpen() 方法會替觸發事件的.panel依照情況的不同,新增或是移除.open 這個 class,若是觸發事件的.panel原本沒有.open則新增,有的話則移除.open

transitionend 事件發生,toggleActive() 會作出如同 toggleOpen() 一樣的判斷,決定新增還是移除.open-active這個 class。因為同時會被觸發的 transitionend 事件有很多個,我們決定在觸發 transitionend 事件的 CSS 屬性是 flex 時,才採取處理。為什麼不寫 e.propertyName == 'flex-grow' 是因為在 Safari 顯示的是 flex 而 Chrome、FireFox 顯示的是 flex-grow,為避免這個差異導致錯誤,我們可以使用 include('flex'),當 propertyName 含有 flex 就進行事件處理。

function toggleOpen(){
    this.classList.toggle('open');
}

function toggleActive(e){
    if(e.propertyName.includes('flex')){
        this.classList.toggle('open-active');
    }
}
補充資料:

Element.classList
String.prototype.includes()

範例網頁請按此


上一篇
Day 4 - Array Cardio Day 1
下一篇
Day 6 - Ajax Type Ahead
系列文
JS30 學習日記31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言